import bisect
from twisted.spread import pb

from option import Option

class Result:
	'Helper class, representing result in single round for one competitor.'

	def __init__(self, competitor):
		self.competitor = competitor
		self.scores = []
		self.best = None
		self.average = None
		self.mean = None

	def append(self, score):
		self.scores.append(score)

		if not self.best or score < self.best:
			self.best = score

		self.calcAvg()
		self.calcMean()

	def calcAvg(self):
		if len(self.scores) > 2:
			self.average = (sum(self.scores) - min(self.scores) - max(self.scores)) / (len(self.scores)-2)
		else:
			self.average = sum(self.scores) / len(self.scores)

	def calcMean(self):
		self.mean = sum(self.scores) / len(scores)

	def __cmp__(self, other):
		return cmp(self.average, other.average)

	def format(self):
		pass

NOT_STARTED = 0
ONGOING = 64
PAUSED = 128
FINISHED = 255

class Round:
	'Class that represents one round in certain event.'

	def __init__(self, title, event, attempts = 5, advancing = 3, comparisonFunc = lambda a,b: cmp(a.average, b.average)):
		self.title = title

		self.competitors = []
		self.scores = {}
		self.standings = [] # just like self.scores, it's sorted though

		self.remaining = 0
		self.advanced = []

		self.attempts = attempts
		self.advancing = advancing
		
		self.phase = NOT_STARTED
		
		self.event = event
		self.comparisonFunc = comparisonFunc

	def addScore(self, competitor, t):
		assert self.phase == ONGOING, 'Round not started.'
		assert competitor in self.scores, 'Competitor not participating in this round.'
		assert len(self.scores[competitor]) < self.attempts, 'All attempts used.'

		t = round(t, 2)
		
		Option().log.debug('User %s, time %f' % (competitor, t))

		self.scores[competitor].append(t)
		self.adjustStandings(competitor)

		self.remaining -= 1

		if not self.remaining:
			self.finish()
			return self.finish()


	# Method used to sort self.standings after score addition.
	# It uses builtin module bisect to adjust standing of
	# just *one* competitor. I believe it works in O(log n).
	def adjustStandings(competitor):
		assert competitor in self.scores, 'Competitor not participating in this round.'

		del self.standings[self.standings.index(self.scores[competitor])]

		bisect.insort(self.standings, self.scores[competitor])
	
	def getAdvanced(self):
		assert self.phase == FINISHED, 'Round is still ongoing.'

		return self.advanced

	def start(self):
		assert self.phase == NOT_STARTED or self.phase == PAUSED, 'Round already ongoing or finished.'

		if not self.standings: # Populating self.standings and self.scores (usually when the round starts for the first time)
			for c in self.competitors:
				x = Result(c)
				self.standings.append(x)
				self.scores[c] = x

		self.phase = ONGOING

	def finish(self):
		assert self.phase == ONGOING or self.phase == PAUSED, 'Round already finished or not even started.'

		self.advanced = self.ppls[:self.advancing]

		self.event.finishRound()

		self.phase = FINISHED

